//Basic Raycasting code for hitscan, line checks, etc
//Original code written by rdude, revised and optimized by Saxashitter and rdude, further revision by LJ, neither are affiliated with nor do they endorse rdude or this mod.
/*
local function ccw(Ax,Bx,Cx,Ay,By,Cy) //nabbed this from a python guide
    return FixedMul((Cy-Ay), (Bx-Ax)) > FixedMul((By-Ay), (Cx-Ax))
end

local function intersect(Ax,Bx,Cx,Dx,Ay,By,Cy,Dy)
    return ccw(Ax,Cx,Dx,Ay,Cy,Dy) != ccw(Bx,Cx,Dx,By,Cy,Dy) and ccw(Ax,Bx,Cx,Ay,By,Cy) != ccw(Ax,Bx,Dx,Ay,By,Dy)
end
*/
/*local function intersect(l1p1x, l1p2x, l2p1x, l2p2x, l1p1y, l1p2y, l2p1y, l2p2y, seg1, seg2) //figured out by somebody MUCH MUCH smarter than me on the love2d forums
	local a1,b1,a2,b2 = l1p2y-l1p1y, l1p1x-l1p2x, l2p2y-l2p1y, l2p1x-l2p2x
	local c1,c2 = a1*l1p1x+b1*l1p1y, a2*l2p1x+b2*l2p1y
	local det,x,y = a1*b2 - a2*b1
	if det==0 then return false end
	x,y = (b2*c1-b1*c2)/det, (a1*c2-a2*c1)/det
	if seg1 or seg2 then
		if seg1 and not (min(l1p1x,l1p2x) <= x and x <= max(l1p1x,l1p2x) and min(l1p1y,l1p2y) <= y and y <= max(l1p1y,l1p2y)) or
			seg2 and not (min(l2p1x,l2p2x) <= x and x <= max(l2p1x,l2p2x) and min(l2p1y,l2p2y) <= y and y <= max(l2p1y,l2p2y)) then
			return false
		end
	end
	print("intersect")
	return x,y
end*/
/*
local function segmentVsAABB(x1, y1, x2, y2, l, t, r, b)
  -- normalize segment
  local dx, dy = x2 - x1, y2 - y1
  local d = FixedSqrt(FixedMul(dx, dx) + FixedMul(dy, dy))
  if d == 0 then
    return false
  end
  local nx, ny = dx/d, dy/d
  -- minimum and maximum intersection values
  local tmin, tmax = 0, d
  -- x-axis check
  if nx == 0 then
    if x1 < l or x1 > r then
      return false
    end
  else
    local t1, t2 = (l - x1)/nx, (r - x1)/nx
    if t1 > t2 then
      t1, t2 = t2, t1
    end
    tmin = max(tmin, t1)
    tmax = min(tmax, t2)
    if tmin > tmax then
      return false
    end
  end
  -- y-axis check
  if ny == 0 then
    if y1 < t or y1 > b then
      return false
    end
  else
    local t1, t2 = (t - y1)/ny, (b - y1)/ny
    if t1 > t2 then
      t1, t2 = t2, t1
    end
    tmin = max(tmin, t1)
    tmax = min(tmax, t2)
    if tmin > tmax then
      return false
    end
  end
  -- points of intersection
  -- one point
  local qx, qy = x1 + nx*tmin, y1 + ny*tmin
  if tmin == tmax then
	print("1")
    return true, qx, qy
  end
  -- two points
  print("2")
  return true, qx, qy, x1 + nx*tmax, y1 + ny*tmax
end

local raycastMobj
addHook("NetVars", function(sync)
	raycastMobj = sync($)
end)

mobjinfo[freeslot "MT_GREENRAY"] = {
	spawnstate = S_THOK,
	radius = FU,
	height = FU,
	flags = MF_NOGRAVITY|MF_NOCLIPTHING
}

local function XYZCollide(x, y, z, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local halfrad = mo.radius
	local pos = mo.x //we're gonna be snobby and reuse this var for both x, y, and z to try and really squeeze out all the optimization 
	if x > pos+(halfrad) then return false end
	if x < pos-(halfrad) then return false end
	local pos = mo.y
	if y > pos+(halfrad) then return false end
	if y < pos-(halfrad) then return false end
	local pos = mo.z
	if z > mo.height+pos then return false end
	if z < pos then return false end
	return true
end

local function LineCollide(x, y, z, ox, oy, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius
	local mA = mo.x - rad //x 1
	local mB = mo.x + rad //x 2
	local mC = mo.y + rad //y
	local bruh = mo.z+mo.height
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	local mC = mo.y - rad //new y
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	local mA = mo.y - rad //y 1
	local mB = mo.y + rad //y 2
	local mC = mo.x + rad //x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	local mC = mo.x - rad //new x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	return false
end

local function DoubleLineCollide(x, y, z, ox, oy, mo, size)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius*size
	local mA = mx - rad //x 1
	local mB = mx + rad //x 2
	local mC = my + rad //y
	local bruh = mo.z+mo.height
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	local mC = my - rad //new y
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	local mA = my - rad //y 1
	local mB = my + rad //y 2
	local mC = mx + rad //x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	local mC = mx - rad //new x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	return false
end
*/
/*local function LineCollide(x, y, z, ox, oy, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius
	local mA = mo.x - rad //x 1
	local mB = mo.x + rad //x 2
	local mC = mo.y + rad //y 1
	local mD = mo.y - rad //y 2
	local bruh = mo.z+mo.height
	P_SpawnMobj(ox, oy, z, MT_SMOKE)
	P_SpawnMobj(x, y, z, MT_SMOKE)
	P_SpawnMobj(mA, mC, bruh, MT_FLAMEPARTICLE)
	P_SpawnMobj(mB, mC, bruh, MT_FLAMEPARTICLE)
	if segmentVsAABB(ox,oy,x,y,mA,mC,mB,mD) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	return false
end*/
/*
local function ParseArg(args, variable, default)
	if not args then
		return default
	end

	if args[variable] == nil then
		return default
	end

	if default ~= nil
	and type(args[variable]) ~= type(default)then
		return default
	end

	return args[variable]
end

rawset(_G, "P_AngleRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local z = ParseArg(args, "z", 0)
	local ang = ParseArg(args, "ang", 0)
	local zang = ParseArg(args, "zang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = FixedMul(cos(ang), cos(zang))
	local vy = FixedMul(sin(ang), cos(zang))
	local vz = sin(zang)
	local ox
	local oy
	local oz

	local move = distance/steps
	
	//local bruhhhh = 0

	for stepcount = 1, steps do
		ox = x
		oy = y
		oz = z
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		z = $ + FixedMul(move, vz)
		
		if callback then
			callback(x, y, z, arg)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, z) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if LineCollide(x, y, z, ox, oy, object) == true then
				table.insert(collisionlist, {x, y, z, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, z, sarg, object)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj) == false then break end
	
		if lclip == true or lclip == 1 then
			local sect = raycastMobj.subsector.sector
			if z < sect.floorheight or z > sect.ceilingheight then //in floor or ceiling
				if not (sect.f_slope or sect.c_slope) then
					if callback2 then
						callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
					end
					break
				end
			end
			local shouldbreak = 0
			for fof in sect.ffloors() do //check fofs
				if not (fof.flags & FOF_SOLID) then continue end
				if (z <= fof.topheight and z >= fof.bottomheight) then
					shouldbreak = 1
					break
				end
			end
			if shouldbreak == 1 then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
			if R_PointInSubsectorOrNil(x, y) == nil then //check walls
				//bruhhhh = $ + 1
				if callback2 then //stjr fix your fucking function brahhhhhh, this returns nil over linedefs.
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)

rawset(_G, "P_MagnetizedAngleRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local z = ParseArg(args, "z", 0)
	local ang = ParseArg(args, "ang", 0)
	local zang = ParseArg(args, "zang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local size = ParseArg(args, "size") //multiplier, 2 will magnetize to twice the normal hitbox, 3 will magnetize to three times the normal hitbox, so on.
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = FixedMul(cos(ang), cos(zang))
	local vy = FixedMul(sin(ang), cos(zang))
	local vz = sin(zang)
	local ox
	local oy
	local oz

	local move = distance/steps

	for stepcount = 1, steps do
		ox = x
		oy = y
		oz = z
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		z = $ + FixedMul(move, vz)
		
		if callback then
			callback(x, y, z, arg)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, z) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			//if object.type ~= MT_PLAYER then return nil end
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if DoubleLineCollide(x, y, z, ox, oy, object, size) == true then
				table.insert(collisionlist, {x, y, z, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, z, sarg, object)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj, raycastMobj.x - 128*FU, raycastMobj.x + 128*FU, raycastMobj.y - 128*FU, raycastMobj.y + 128*FU) == false then break end //the xxyy values are a hacky fix for an issue too long to explain in a single line comment.
//, raycastMobj.x - 128*FU*(size-1), raycastMobj.x + 128*FU*(size-1), raycastMobj.y - 128*FU*(size-1), raycastMobj.y + 128*FU*(size-1)
		if lclip == true or lclip == 1 then
			local sect = raycastMobj.subsector.sector
			if z < sect.floorheight or z > sect.ceilingheight then
				if not (sect.f_slope or sect.c_slope) then
					if callback2 then
						callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
					end
					break
				end
			end
			local shouldbreak = 0
			for fof in sect.ffloors() do
				if not (fof.flags & FOF_SOLID) then continue end
				if (z <= fof.topheight and z >= fof.bottomheight) then
					shouldbreak = 1
					break
				end
			end
			if shouldbreak == 1 then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
			if R_PointInSubsectorOrNil(x, y) == nil then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)
*/
//COM_AddCommand("raytest", function(bruh, x, y, z, ang, zang, step, amount)
//	P_AngleRaycast(tonumber(x)*FU, tonumber(y)*FU, tonumber(z)*FU, FixedAngle(tonumber(ang)*FU), FixedAngle(tonumber(zang)*FU), tonumber(step), tonumber(amount), P_SpawnMobj, nil, MT_THOK, nil, true, true)
//end, COM_ADMIN)